#include "app_buffer.h"
#include "log.h"
#include "string.h"
#include "stdlib.h"
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

typedef struct
{
    int32_t len;
    uint8_t first_byte;
} Element;

/**
 * 初始化缓冲区
 * @param 其中一份缓冲区的大小 实际双缓冲 会占用total_size*2的空间
 *
 *  //1  创建大缓冲区 开辟内存
    //2  创建小缓冲区 开辟内存  创建两个  每个u单独的内存储 空间大小 ，数据a长度给0
    //3   指定哪个为读缓冲区  哪个为写缓冲区
    //4  锁初始化
 */

Buffer *app_buffer_init(int32_t total_size)
{
    // 1  创建大缓冲区 开辟内存
    Buffer *buffer = (Buffer *)malloc(sizeof(Buffer));

    // 2  创建小缓冲区 开辟内存  创建两个  每个u单独的内存储 空间大小 ，数据a长度给0
    for (uint8_t i = 0; i < 2; i++)
    {
        SubBuffer *sub_buffer = (SubBuffer *)malloc(sizeof(SubBuffer));
        sub_buffer->ptr = malloc(total_size);
        sub_buffer->total_size = total_size;
        sub_buffer->head_index = 0;
        sub_buffer->tail_index = 0;
        buffer->sub_buffers[i] = sub_buffer;
    }

    // 3   指定哪个为读缓冲区  哪个为写缓冲区
    buffer->read_index = 0;
    buffer->write_index = 1;

    // 4  锁初始化
    pthread_mutex_init(&buffer->read_lock, NULL);
    pthread_mutex_init(&buffer->swap_lock, NULL);

    return buffer;
}

/**
 *  向缓冲区写入
 * @return 0 成功 -1 失败
 *  1  获取写缓冲区
 *  2  校验长度是否足够写入
 *  3  写入数据
 */
int32_t app_buffer_write(Buffer *buffer, void *write_data, int32_t write_len)
{
    log_info("缓冲区写入开始 加锁!");
    pthread_mutex_lock(&buffer->swap_lock);

    log_info("缓冲区写入开始!");

    // *  1  获取写缓冲区
    SubBuffer *write_sub_buffer = buffer->sub_buffers[buffer->write_index];
    //*  2  校验长度是否足够写入
    if (write_len + sizeof(int32_t) > write_sub_buffer->total_size - write_sub_buffer->tail_index)
    {
        log_error("待写入长度超过剩余写入空间!");
        pthread_mutex_unlock(&buffer->swap_lock);
        return -1;
    }
    //*  3  写入数据  + a长度增加
    Element *element = (Element *)(write_sub_buffer->ptr + write_sub_buffer->tail_index);
    element->len = write_len;
    memcpy(&element->first_byte, write_data, write_len);
    write_sub_buffer->tail_index += write_len + sizeof(int32_t);

    pthread_mutex_unlock(&buffer->swap_lock);

    return 0;
}

/**
 *  从缓冲区读取
 *  @param 1 缓冲区 2 写入的目标空间(已分配空间) ,3 可用空间
 *  @return 最终实际读取的数据量
 *   1  获取读缓冲区
 *   2  获取在当前读缓冲区可读的长度  （从可用空间和当前读缓冲区已有数据  取较小值）
 *   3  进行读取数据
 *   4  只要读缓冲区可读的长度等于0  进行读写缓冲区的切换
 *   5  再次获取在当前读缓冲区可读的长度  （从剩余可用空间和当前读缓冲区已有数据  取较小值）
 *   6  再次读取数据
 *   7  返回最终实际读取的数据量
 */
int32_t app_buffer_read(Buffer *buffer, void *read_buff, int32_t read_buff_size)
{ // A  此处加锁 因为下面的操作设计到了 公共数据
    log_info("缓冲区读取开始 加锁!");
    pthread_mutex_lock(&buffer->read_lock);
    // *   1  获取读缓冲区
    SubBuffer *read_sub_buffer = buffer->sub_buffers[buffer->read_index];

    // 2 验证当前buffer是否读取完
    if (read_sub_buffer->head_index == read_sub_buffer->tail_index)
    {
        // 写缓存区也是空的，没有数据可以读取
        // 这里可以不用加swap锁，因为没有发生任何数据更改
        if (buffer->sub_buffers[buffer->write_index]->tail_index == 0)
        {
            log_warn("缓冲区为空!");
            pthread_mutex_unlock(&buffer->read_lock);
            return 0;
        }
        log_info("交换缓冲区！");

        // 当前buffer已经读取空，交换缓存
        pthread_mutex_lock(&buffer->swap_lock);
        // 将读写索引清零
        read_sub_buffer->head_index = 0;
        read_sub_buffer->tail_index = 0;
        // 1  读写下标号的交换
        int tmp_index = buffer->read_index;
        buffer->read_index = buffer->write_index;
        buffer->write_index = tmp_index;
        pthread_mutex_unlock(&buffer->swap_lock);
        // 2  获取新的读缓冲区
        read_sub_buffer = buffer->sub_buffers[buffer->read_index];
    }

    Element *element = (Element *)(read_sub_buffer->ptr + read_sub_buffer->head_index);
    if (element->len > read_buff_size)
    {
        // 读取空间不够
        log_error("read_buff不够大");
        pthread_mutex_unlock(&buffer->read_lock);
        return -1;
    }
    memcpy(read_buff, &element->first_byte, element->len);
    read_sub_buffer->head_index += element->len + sizeof(int32_t);
    int32_t result = element->len;
    pthread_mutex_unlock(&buffer->read_lock);
    log_info("缓冲区读取结束 解锁!");
    //*   7  返回最终实际读取的数据量
    return result;
}

/**
 * i释放缓冲u区
 */
void app_buffer_free(Buffer *buffer)
{ // 1  释放所有子缓冲区的数据j空间
    free(buffer->sub_buffers[0]->ptr);
    free(buffer->sub_buffers[1]->ptr);
    // 2  释放所有子缓冲区结构体空间
    free(buffer->sub_buffers[0]);
    free(buffer->sub_buffers[1]);
    // 3  释放主缓冲区结构体空间
    free(buffer);
}